//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Diagnostics.Contracts;
using System.IO;
using OpenLS.Spreadsheet.Expressions;
using OpenLS.Spreadsheet.Formats.Biff.Records;

namespace OpenLS.Spreadsheet.Formats.Biff.Tokens
{
    internal class AreaToken : AreaTokenBase
    {
        private RgceArea RgceArea;

        public AreaToken(AccessorType type): base (type)
        {
        }

        public AreaToken(AccessorType type, RgceArea address, ParsedFormulaBase context)
            : base(type)
        {
            Contract.Assert(! (context is SharedParsedFormula));
            this.RgceArea = address;
        }
        public override FormulaTokenId TokenId
        {
            get { switch (_type)
            {
                case AccessorType.Array:
                    return FormulaTokenId.AreaA;
                    case AccessorType.Reference:
                    return FormulaTokenId.Area;
                    case AccessorType.Value:
                    return FormulaTokenId.AreaV;
                default:
                    throw new NotSupportedException();
            }}
        }

        internal override void ReadContent(FormulaReadContext context)
        {
            this.RgceArea.Read(context.Reader);
        }

        internal override Expression GetExpression(FormulaEvaluationContext context)
        {
            Worksheet ws = context.Worksheet;
            RangeReference rr = new MultiCellsRangeReference(
                ToReference (RgceArea.FirstRowIndex, RgceArea.FirstColumnIndex, RgceArea.IsFirstRowRelative, RgceArea.IsFirstColumnRelative, ws),
                ToReference (RgceArea.LastRowIndex, RgceArea.LastColumnIndex, RgceArea.IsLastRowRelative, RgceArea.IsLastColumnRelative, ws)
                );
            return new RangeReferenceExpression(rr, context.Worksheet);
             
        }

        public override void WriteContent(FormulaWriteContext context)
        {
            this.RgceArea.Write(context.Writer);
        }
        static internal CellReference ToReference(int rowIndex, int columnIndex, bool isRelativeRow, bool isRelativeColumn, Worksheet ws)
        {
            Contract.Requires(ws != null);
            return new StaticCellReference(
                new AnchoredRowReference(ws.Rows[rowIndex], !isRelativeRow),
                new AnchoredColumnReference(ws.Columns[columnIndex], !isRelativeColumn));
        }


    }

    internal struct RgceArea
    {
        public RgceArea(ushort rowFirst, ushort columnFirst, bool isFirstRowRelative, bool isFirstColumnRelative,
            ushort rowLast, ushort columnLast, bool isLastRowRelative, bool isLastColumnRelative)
        {
            this.rowFirst = rowFirst;
            this.columnFirst = new ColRelU(columnFirst, isFirstRowRelative, isFirstColumnRelative);
            this.rowLast = rowLast;
            this.columnLast = new ColRelU(columnLast, isLastRowRelative, isLastColumnRelative);
        }
        public void Read(BinaryReader reader)
        {
            this.rowFirst = reader.ReadUInt16();
            this.rowLast = reader.ReadUInt16();
            columnFirst.Read(reader);
            columnLast.Read(reader);
        }

        private ColRelU columnFirst;
        private ColRelU columnLast;
        private ushort rowFirst;
        private ushort rowLast;

        public void Write(BinaryWriter writer)
        {
            writer.Write(rowFirst);
            writer.Write(rowLast);
            columnFirst.Write(writer);
            columnLast.Write(writer);
        }

        public int FirstRowIndex
        {
            get { return rowFirst; }
        }

        public int FirstColumnIndex
        {
            get { return columnFirst.Col; }
        }

        public bool IsFirstRowRelative
        {
            get { return columnFirst.RowRelative; }
        }

        public bool IsFirstColumnRelative
        {
            get { return columnFirst.ColRelative; }
        }

        public int LastRowIndex
        {
            get { return rowLast;
                ; }
        }

        public int LastColumnIndex
        {
            get { return columnLast.Col; }
        }

        public bool IsLastRowRelative
        {
            get { return columnLast.RowRelative; }
        }

        public bool IsLastColumnRelative
        {
            get { return columnLast.ColRelative; }
        }
    }

    internal struct ColRelU
    {
        private ushort col;
        private bool colRelative;
        private bool rowRelative;

        public ColRelU(ushort column, bool isRowRelative, bool isColumnRelative)
        {
            this.col = column;
            this.colRelative = isColumnRelative;
            ;
            this.rowRelative = isRowRelative;
        }

        public ushort Col
        {
            get { return col; }
        }

        public bool ColRelative
        {
            get { return colRelative; }
        }

        public bool RowRelative
        {
            get { return rowRelative; }
        }

        public void Read (BinaryReader reader)
        {
            ushort u = reader.ReadUInt16();
            this.col = (ushort) (u & 0x3FFF);
            this.colRelative = (u & 0x4000) != 0;
            this.rowRelative = (u & 0x8000) != 0;
        }

        public void Write(BinaryWriter writer)
        {
            ushort u = Col;
            if (ColRelative)
                u |= 0x4000;
            if (RowRelative)
                u |= 0x8000;
            writer.Write(u);
        }
    }
}